package com.ifast.sys.service.impl;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.enums.SqlLike;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ifast.api.util.JWTUtil;
import com.ifast.common.base.CoreServiceImpl;
import com.ifast.common.domain.Tree;
import com.ifast.common.exception.IFastException;
import com.ifast.common.service.RedisParentManager;
import com.ifast.common.type.EnumErrorCode;
import com.ifast.common.utils.BuildTree;
import com.ifast.common.utils.Const;
import com.ifast.common.utils.ExcelEasyUtil;
import com.ifast.common.utils.ExePoolUtil;
import com.ifast.common.utils.FileType;
import com.ifast.common.utils.FileUtil;
import com.ifast.common.utils.GjpLogger;
import com.ifast.common.utils.ImageUtils;
import com.ifast.common.utils.MD5Utils;
import com.ifast.common.utils.WorkId;
import com.ifast.common.utils.alioss.UploadResult;
import com.ifast.common.utils.alioss.UploadUtils;
import com.ifast.oss.domain.FileDO;
import com.ifast.oss.service.FileService;
import com.ifast.sys.dao.DeptDao;
import com.ifast.sys.dao.UserDao;
import com.ifast.sys.dao.UserRoleDao;
import com.ifast.sys.domain.DeptDO;
import com.ifast.sys.domain.SysUserRole;
import com.ifast.sys.domain.UserDO;
import com.ifast.sys.domain.UserDOImport;
import com.ifast.sys.domain.UserRoleDO;
import com.ifast.sys.service.UserService;
import com.ifast.sys.vo.UserVO;
import com.luculent.util.extension.api.Result;
import com.xiaoleilu.hutool.date.DatePattern;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.util.CollectionUtil;
import com.xiaoleilu.hutool.util.StrUtil;

/**
 * <pre>
 * </pre>
 * 
 * <small> 2018年3月23日 | Aron</small>
 */
@Transactional
@Service("sysUserServiceImpl")
public class UserServiceImpl extends CoreServiceImpl<UserDao, UserDO> implements UserService {

    @Autowired
    UserRoleDao userRoleMapper;

    @Autowired
    DeptDao deptMapper;

    @Autowired
    private FileService sysFileService;

    @Autowired
    private RedisParentManager redisParentService;

    @Autowired
    private com.ifast.api.service.UserService userService;

    @Override
    public UserDO selectById(Serializable id) {
        List<Long> roleIds = userRoleMapper.listRoleId(id);
        UserDO user = baseMapper.selectById(id);
        user.setDeptName(deptMapper.selectById(user.getDeptId()).getName());
        user.setRoleIds(roleIds);
        return user;
    }

    @Transactional
    @Override
    public boolean insert(UserDO user){
        boolean flag = this.clInsertSl(user.getDwCode(),1);
        if(!flag){
            return flag;
        }
        int count = baseMapper.insert(user);
        Long userId = user.getId();
        List<Long> roles = user.getRoleIds();
        userRoleMapper.removeByUserId(userId);
        List<UserRoleDO> list = new ArrayList<>();
        for (Long roleId : roles) {
            UserRoleDO ur = new UserRoleDO();
			try {
				ur.setId(WorkId.sortUID());
			} catch (Exception e) {
				throw new RuntimeException("user_role_id 生成异常");
			}
            ur.setUserId(userId.toString());
            ur.setRoleId(roleId.toString());
            userRoleMapper.insert(ur);
            list.add(ur);
        }
//        if (list.size() > 0) {
//            userRoleMapper.batchSave(list);
//        }
        return retBool(count);
    }

    /**
     * 判断是否超出人数限制
     * @param dwCode
     * @param addSl
     * @return
     */
    private boolean clInsertSl(String dwCode,int addSl){
        //获取人员数量
        if(dwCode.length() != 4){
            String dwCodez = dwCode.substring(0,8);
            Wrapper wrapperUser = new EntityWrapper<UserDO>();
            wrapperUser.like("dw_code",dwCodez,SqlLike.RIGHT);
            Integer zrs = this.selectCount(wrapperUser);
            //总人数
            DeptDO wrapperDept = new DeptDO();
            wrapperDept.setDwCode(dwCodez);
            DeptDO deptDO1 = this.deptMapper.selectOne(wrapperDept);
            return Math.subtractExact(deptDO1.getQyCount(),zrs+addSl) > 0;
        }else {
            return true;
        }
    }


    @Override
    public boolean updateById(UserDO user) {
        int r = baseMapper.updateById(user);
        Long userId = user.getId();
        List<Long> roles = user.getRoleIds();
        userRoleMapper.removeByUserId(userId);
        List<UserRoleDO> list = new ArrayList<>();
        for (Long roleId : roles) {
            UserRoleDO ur = new UserRoleDO();
            ur.setUserId(userId.toString());
            ur.setRoleId(roleId.toString());
            list.add(ur);
        }
        if (list.size() > 0) {
            userRoleMapper.batchSave(list);
        }
        return retBool(r);
    }

    @Override
    public boolean deleteById(Serializable userId) {
        userRoleMapper.removeByUserId(userId);
        return retBool(baseMapper.deleteById(userId));
    }

    @Override
    public boolean exit(Map<String, Object> params) {
        return retBool(baseMapper.selectByMap(params).size());
    }

    @Override
    public Set<String> listRoles(Long userId) {
        return null;
    }

    @Override
    public int resetPwd(UserVO userVO, UserDO userDO) {
        if (Objects.equals(userVO.getUserDO().getId(), userDO.getId())) {
            if (Objects.equals(MD5Utils.encrypt(userDO.getUsername(), userVO.getPwdOld()), userDO.getPassword())) {
                userDO.setPassword(MD5Utils.encrypt(userDO.getUsername(), userVO.getPwdNew()));
                return baseMapper.updateById(userDO);
            } else {
                throw new IFastException("输入的旧密码有误！");
            }
        } else {
            throw new IFastException("你修改的不是你登录的账号！");
        }
    }

    @Override
    public int adminResetPwd(UserVO userVO) {
        UserDO userDO = selectById(userVO.getUserDO().getId());
        if ("admin".equals(userDO.getUsername())) {
            throw new IFastException(EnumErrorCode.userUpdatePwd4adminNotAllowed.getCodeStr());
        }
        userDO.setPassword(MD5Utils.encrypt(userDO.getUsername(), userVO.getPwdNew()));
        return baseMapper.updateById(userDO);

    }

    @Transactional
    @Override
    public boolean deleteBatchIds(List<? extends Serializable> idList) {
        int count = baseMapper.deleteBatchIds(idList);
        userRoleMapper.deleteBatchIds(idList);
        return retBool(count);
    }

    @Override
    public Tree<DeptDO> getTree() {
        List<Tree<DeptDO>> trees = new ArrayList<Tree<DeptDO>>();
        List<DeptDO> depts = deptMapper.selectList(null);
        Long[] pDepts = deptMapper.listParentDept();
        Long[] uDepts = baseMapper.listAllDept();
        Long[] allDepts = (Long[]) ArrayUtils.addAll(pDepts, uDepts);
        for (DeptDO dept : depts) {
            if (!ArrayUtils.contains(allDepts, dept.getId())) {
                continue;
            }
            Tree<DeptDO> tree = new Tree<DeptDO>();
            tree.setId(dept.getId().toString());
            tree.setParentId(dept.getParentId().toString());
            tree.setText(dept.getName());
            Map<String, Object> state = new HashMap<>(16);
            state.put("opened", true);
            state.put("mType", "dept");
            tree.setState(state);
            trees.add(tree);
        }
        List<UserDO> users = baseMapper.selectList(null);
        for (UserDO user : users) {
            Tree<DeptDO> tree = new Tree<DeptDO>();
            tree.setId(user.getId().toString());
            tree.setParentId(user.getDeptId().toString());
            tree.setText(user.getName());
            Map<String, Object> state = new HashMap<>(16);
            state.put("opened", true);
            state.put("mType", "user");
            tree.setState(state);
            trees.add(tree);
        }
        // 默认顶级菜单为０，根据数据库实际情况调整
        Tree<DeptDO> t = BuildTree.build(trees);
        return t;
    }

    @Override
    public int updatePersonal(UserDO userDO) {
        return baseMapper.updateById(userDO);
    }

    @Override
    public Map<String, Object> updatePersonalImg(MultipartFile file, String avatar_data, Long userId) throws Exception {
        String fileName = file.getOriginalFilename();
        fileName = FileUtil.renameToUUID(fileName);
        String url = "";

        // 获取图片后缀
        String prefix = fileName.substring((fileName.lastIndexOf(".") + 1));
        String[] str = avatar_data.split(",");
        // 获取截取的x坐标
        int x = (int) Math.floor(Double.parseDouble(str[0].split(":")[1]));
        // 获取截取的y坐标
        int y = (int) Math.floor(Double.parseDouble(str[1].split(":")[1]));
        // 获取截取的高度
        int h = (int) Math.floor(Double.parseDouble(str[2].split(":")[1]));
        // 获取截取的宽度
        int w = (int) Math.floor(Double.parseDouble(str[3].split(":")[1]));
        // 获取旋转的角度
        int r = Integer.parseInt(str[4].split(":")[1].replaceAll("}", ""));
        try {
            BufferedImage cutImage = ImageUtils.cutImage(file, x, y, w, h, prefix);
            BufferedImage rotateImage = ImageUtils.rotateImage(cutImage, r);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(rotateImage, prefix, out);
            // 转换后存入数据库
            byte[] b = out.toByteArray();
            url = sysFileService.upload(b, fileName);
        } catch (Exception e) {
            throw new IFastException("图片裁剪错误！！");
        }
        Map<String, Object> result = new HashMap<>();
        FileDO sysFile = new FileDO(FileType.fileType(fileName), url, new Date());
        if (sysFileService.insert(sysFile)) {
            UserDO userDO = new UserDO();
            userDO.setId(userId);
            userDO.setPicId(sysFile.getId());
            if (retBool(baseMapper.updateById(userDO))) {
                result.put("url", sysFile.getUrl());
            }
        }
        return result;
    }

	@Override
	public void saveUserRole(SysUserRole sysUserRole) {
		 
	}


    /**
     *
     * @param token
     * @return
     */
    @Override
    public Result<?> getUserInfoForApp(String token){
        Result<Map<String,Object>> result = new Result<>();
        String userId = JWTUtil.getUserId(token);
        UserDO userDO = this.selectById(userId);
        if(userDO != null){
        	Map<String,Object> data = Maps.newHashMap();
        	data.put("name",userDO.getName());
            FileDO fileDO = this.sysFileService.selectById(userDO.getPicId());
            data.put("picUrl",fileDO!=null?fileDO.getUrl():"");
            DeptDO deptDO = this.deptMapper.selectById(userDO.getDeptId());
            data.put("deptName",deptDO!=null?deptDO.getName():"");
            result.setData(data);
        }
        return result;
    }

    /**
     * 跟新密码
     * @param token
     * @param oldPassword
     * @param newPassword
     * @return
     */
    @Override
    public Result<?> updatePassword(String token, String oldPassword, String newPassword){
        Result<?> result = new Result<>();
        String userId = JWTUtil.getUserId(token);
        UserDO userDO = this.selectById(userId);
        if(userDO != null &&  userDO.getPassword().equals(MD5Utils.encrypt(userDO.getUsername(), oldPassword))){
            userDO.setPassword(MD5Utils.encrypt(userDO.getUsername(), newPassword));
            this.baseMapper.updateById(userDO);
            userService.logoutToken(token,"");
        }else{
            result.setCode(EnumErrorCode.apiYwClFailed.getCode());
            result.setMsg("旧密码错误!");
        }
        return result;
    }


    /**
     * 修改基本信息
     * @param token
     * @param request
     * @return
     */
    @Override
    public Result<?> updateUserInfoZjForApp(String token,HttpServletRequest request) throws IOException {
        Result<Map<String,Object>> result = new Result<>();
        UploadResult uploadResult = UploadUtils.uploadOssPic(request);
        if(uploadResult.isSuccess()){
            List<String> listImg = uploadResult.getFileURLs();
            FileDO sysFile = new FileDO(FileType.fileType("2.jpg"), listImg.get(0), new Date());
            if (sysFileService.insert(sysFile)) {
                String userId = JWTUtil.getUserId(token);
                UserDO userDO = this.selectById(userId);
                userDO.setPicId(sysFile.getId());
                if (retBool(baseMapper.updateById(userDO))) {
                	Map<String,Object> data = Maps.newHashMap();
                	data.put("picUrl", sysFile.getUrl());
                    result.setData(data);
                }
            }else{
                result.setCode(EnumErrorCode.apiAuthorizationLoginFailed.getCode());
                result.setMsg(EnumErrorCode.apiYwFailed.getMsg());
            }
        }
        return result;
    }


    @Override
    public Result<?> forgetPassword(String code,String userName,String newPassWord) {
        Result<?> Result = new Result<>();
        String codez = redisParentService.getValue(userName);
        if(!Objects.equals(codez,code)){
            Result.setCode(EnumErrorCode.apiYwClFailed.getCode());
            Result.setMsg("验证码填写不正确!");
            return Result;
        }
        Wrapper<UserDO> wrapper = new EntityWrapper<>();
        wrapper.eq("username",userName);
        UserDO userDOOld = this.selectOne(wrapper);
        if(userDOOld != null){
            userDOOld.setPassword(MD5Utils.encrypt(userDOOld.getUsername(), newPassWord));
            this.baseMapper.updateById(userDOOld);
            redisParentService.removeValue(userName);
        }else{
            Result.setCode(EnumErrorCode.apiYwClFailed.getCode());
            Result.setMsg(EnumErrorCode.apiYwClFailed.getMsg());
        }
        return  Result;
    }


    @Override
    public Page<UserDO> listPage(Page<UserDO> page, UserDO userDO){
        return page.setRecords(baseMapper.listPage(page,userDO));
    }


    @Override
    public List<UserDO> listUser(UserDO userDO){
        return baseMapper.listUser(userDO);
    }

    /**
     * 导入用户,
     * 使用了线程池异步处理，尽量不要在线程池中做数据库插入等涉及事务的操作，如果这样可能会造成事务不好使
     * 或者不好处理，让异步处理数据，最后统一做入库操作
     * @param file
     * @return
     */
    @Override
    public Map<String,Integer> importUser(MultipartFile file,DeptDO deptDOCurr) throws ExecutionException, InterruptedException {
        Map<String,Integer> mapRet = Maps.newHashMap();
        mapRet.put("ck",0);
        mapRet.put("zs",0);
        List<UserDOImport> userDOImportList = ExcelEasyUtil.importExcel(file,0,1,UserDOImport.class);
        if(CollectionUtil.isEmpty(userDOImportList)){
            return mapRet;
        }
        int importSize = userDOImportList.size();
        boolean flagRet = clInsertSl(deptDOCurr.getDwCode(),importSize);
        if(!flagRet){
            mapRet.put("zs",importSize);
            return mapRet;
        }
        Wrapper<DeptDO> wrapperD = new EntityWrapper<>();
        wrapperD.like("dw_code",new StringBuilder().append(deptDOCurr.getDwCode()).append("%").toString());
        List<DeptDO> deptDOList = this.deptMapper.selectList(wrapperD);
        if(CollectionUtil.isEmpty(deptDOList)){
            return mapRet;
        }
        //获取本单位能导入那些单位数据
        Map<String,DeptDO> mapDept = deptDOList.stream().collect(Collectors.toMap(DeptDO::getName, (deptDo)-> deptDo));
        //对导入数据去重
        List<UserDOImport> uniqueList = userDOImportList.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(UserDOImport::getUserName))), ArrayList::new)
        );
        //对导入数据分组
        Map<String,List<UserDOImport>> mapListImport = uniqueList.stream().collect(Collectors.groupingBy(UserDOImport::getXb));
        CompletableFuture<List<UserDO>> completableFuture = CompletableFuture.supplyAsync(()->{
            List<UserDO> userDOListNan = Lists.newArrayList();
            List<UserDOImport> listNan = mapListImport.get("男");
            clImportUser(mapDept, userDOListNan, listNan);
            return userDOListNan;
        },ExePoolUtil.getIns());

        CompletableFuture<List<UserDO>> completableFuturev = completableFuture.thenCombine(CompletableFuture.supplyAsync(()->{
            List<UserDO> userDOListNv = Lists.newArrayList();
            List<UserDOImport> listNv = mapListImport.get("女");
            clImportUser(mapDept, userDOListNv, listNv);
            return userDOListNv;
        },ExePoolUtil.getIns()),(r1,r2)->{
            List<UserDO> listNew = Lists.newArrayList();
            listNew.addAll(r1);
            listNew.addAll(r2);
            return listNew;
        }).handleAsync((res,ex)->{
            if(ex != null) {
                GjpLogger.error(ex.toString(),ex);
                throw new RuntimeException(ex);
            }
            return res;
        });

        /**
         * 等待所有数据都执行才返回
         */
        List<UserDO> listInsert =  completableFuturev.get();
        if(CollectionUtil.isEmpty(listInsert)){
            return mapRet;
        }
        boolean flag = this.insertBatch(listInsert);
        if(!flag){
            return mapRet;
        }
        mapRet.put("ck",listInsert.size());
        mapRet.put("zs",importSize);
        return mapRet;
    }

    /**
     * 处理导入用户数据
     * @param mapDept
     * @param userDOListNan
     * @param listNan
     */
    private void clImportUser(Map<String, DeptDO> mapDept, List<UserDO> userDOListNan,  List<UserDOImport> listNan) {
        if(CollectionUtil.isNotEmpty(listNan)){
            for (UserDOImport userDOImport : listNan) {
                Wrapper<UserDO> wrapperD = new EntityWrapper<>();
                wrapperD.eq("username",userDOImport.getUserName());
                int zsz = this.selectCount(wrapperD);
                if(mapDept.containsKey(userDOImport.getDwName()) && zsz == 0){
                    DeptDO deptDO = mapDept.get(userDOImport.getDwName());
                    UserDO userDO = new UserDO();
                    userDO.setName(userDOImport.getXm());
                    userDO.setMobile(userDOImport.getUserName());
                    userDO.setDeptId(deptDO.getId());
                    userDO.setDwCode(deptDO.getDwCode());
                    userDO.setPassword(MD5Utils.encrypt(userDOImport.getUserName(), "paf123"));
                    userDO.setUsername(userDOImport.getUserName());
                    if(StrUtil.isNotBlank(userDOImport.getCjgzsj())){
                        userDO.setCjgzsj(DateUtil.format(DateUtil.calendar(new Date(userDOImport.getCjgzsj()).getTime()).getTime(),DatePattern.NORM_DATE_PATTERN));
                    }else{
                        userDO.setCjgzsj("");
                    }
                    userDO.setGonghao(userDOImport.getGongHao());
                    userDO.setZw(userDOImport.getZw());
                    userDO.setCreateTime(DateUtil.now());
                    userDO.setStatus(Const.DelState.del.getValue());
                    userDO.setYhlx(Const.ptyh);
                    userDO.setSex(com.google.common.base.Objects.equal("男",userDOImport.getXb()) ? Const.DelState.del.getValue():Const.DelState.ndel.getValue());
                    userDOListNan.add(userDO);
                }
            }
        }
    }
}
